package com.yumeng.framework.persistence.mybatis.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.*;
import com.yumeng.common.utils.SpringUtils;
import com.yumeng.config.BaseConfiguration;
import com.yumeng.config.helper.EnvHelper;
import com.yumeng.config.properties.YumengMybatisPlusProperties;
import com.yumeng.framework.persistence.mybatis.handlers.MyMetaObjectHandler;
import com.yumeng.framework.persistence.mybatis.helper.CustomIdGenerator;
import com.yumeng.framework.persistence.mybatis.injector.MySqlInjector;
import com.yumeng.framework.persistence.mybatis.plugins.dynamictable.SimpleTableNameHandler;
import com.yumeng.framework.persistence.mybatis.plugins.dynamictable.TableNameDateSuffixType;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 *
 * @author wxd
 * @date 2025/10/15 15:53
 */
@Slf4j
public abstract class MybatisPlusConfiguration extends BaseConfiguration {

    static {
        //====================================本地缓存 SQL 解析
        // 默认支持序列化 FstSerialCaffeineJsqlParseCache，JdkSerialCaffeineJsqlParseCache
/*        JsqlParserGlobal.setJsqlParseCache(new JdkSerialCaffeineJsqlParseCache(
                (cache) -> cache.maximumSize(1024)
                        .expireAfterWrite(5, TimeUnit.SECONDS))
        );*/
        //====================================设置 SQL 解析线程池 默认创建固定线程池核心数: (Runtime.getRuntime().availableProcessors() + 1) / 2
        //JsqlParserGlobal.setExecutorService(....);
        //====================================设置 JsqlParser解析处理方法
/*        JsqlParserGlobal.setParserMultiFunc((sql)-> {
            log.info("解析SQL:{}", sql);
            return CCJSqlParserUtil.parseStatements(sql, JsqlParserGlobal.getExecutorService(), null);
        });
        JsqlParserGlobal.setParserSingleFunc((sql)-> {
            log.info("解析SQL:{}", sql);
            return CCJSqlParserUtil.parse(sql, JsqlParserGlobal.getExecutorService(), null);
        });*/
    }

    private YumengMybatisPlusProperties mybatisPlusProperties;

    public MybatisPlusConfiguration(){
        mybatisPlusProperties = SpringUtils.getBean(YumengMybatisPlusProperties.DEFAULT_BEAN_NAME);
    }

    //Sequence主键 暂不研究
/*    @Bean
    public IKeyGenerator keyGenerator() {
        return new H2KeyGenerator();
    }*/

/*    @Bean
    public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
        return plusProperties -> plusProperties.getGlobalConfig().getDbConfig().setKeyGenerator(new H2KeyGenerator());
    }*/

    /**
     * 主数据库类型
     *
     * @return
     */
    protected abstract DbType mainDbType();

    /**
     * 获取当前租户id
     *
     * @return
     */
    protected Long getCurrTenantId(){
        return null;
    }

    /**
     * 多租户忽略表
     *
     * @return
     */
    protected List<String> getTenantIgnoreTables(){
        return Collections.emptyList();
    }

    /**
     * 动态表名映射（日期模式）
     * 表名-> 日期类型
     *
     * @return
     */
    protected Map<String, TableNameDateSuffixType> getDynamicTableNameMapForDateMode(){
        return null;
    }
    /**
     * 动态表名映射（idMod模式）
     * 表名-> 分几张表
     *
     * @return
     */
    protected Map<String, Integer> getDynamicTableNameMapForIdModMode(){
        return null;
    }

    /**
     * 自定义sql注入
     * @return
     */
    @Bean
    public AbstractSqlInjector sqlInjector() {
        return new MySqlInjector();
    }

    /**
     * 动态填充
     * @return
     */
    @Bean
    public MetaObjectHandler metaObjectHandler(){
        return new MyMetaObjectHandler();
    }

    /**
     * ID生成
     * @return
     */
    @Bean
    public IdentifierGenerator idGenerator() {
        long workerId = mybatisPlusProperties.getWorkerId();
        long datacenterId = mybatisPlusProperties.getDatacenterId();

        CustomIdGenerator gen = new CustomIdGenerator(workerId, datacenterId);
        log.info("ID生成器：{},workerId:{},datacenterId:{}",gen.idWorker.getClass().getName(), workerId, datacenterId);
        return gen;
        //return new DefaultIdentifierGenerator(0,0);
    }

    @Bean
    @ConditionalOnProperty(name = "yumeng.mybatis-plus.plugins.enable-dynamic-table-name")
    public SimpleTableNameHandler simpleTableNameHandler(){
        SimpleTableNameHandler handler = new SimpleTableNameHandler(getDynamicTableNameMapForIdModMode(), getDynamicTableNameMapForDateMode());
        return handler;
    }

    /**
     * 使用多个功能需要注意顺序关系,建议使用如下顺序
     *
     *     多租户,动态表名
     *     分页,乐观锁
     *     sql 性能规范,防止全表更新与删除
     *
     * 总结: 对 sql 进行单次改造的优先放入,不对 sql 进行改造的最后放入
     *      开启多租户、分页插件时，如果存在join，必须写表别名
     *
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //多租户插件
        if (mybatisPlusProperties.getPlugins().getEnableTenant()) {
            TenantLineInnerInterceptor tenantLineInnerInterceptor = new TenantLineInnerInterceptor(new TenantLineHandler() {
                @Override
                public Expression getTenantId() {
                    return new LongValue(getCurrTenantId());
                }
                @Override
                public boolean ignoreTable(String tableName) {
                    return getTenantIgnoreTables().contains(tableName);
                }
            });
            interceptor.addInnerInterceptor(tenantLineInnerInterceptor);
        }

        //动态表名插件
        if (mybatisPlusProperties.getPlugins().getEnableDynamicTableName()){
            DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor(simpleTableNameHandler());
            interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
        }

        //分页插件
        //如果配置多个插件, 切记分页最后添加
        //如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(mainDbType()));

        /**
         * 乐观锁插件:
         *     支持的数据类型包括：int, Integer, long, Long, Date, Timestamp, LocalDateTime。
         *     对于整数类型，newVersion 是 oldVersion + 1。
         *     newVersion 会自动回写到实体对象中。
         *     支持内置的 updateById(entity) 和 update(entity, wrapper), saveOrUpdate(entity), insertOrUpdate(entity) (version >=3.5.7) 方法。
         *     自定义方法更新时如果满足内置参数的参数条件方式也会执行乐观锁逻辑，例如自定义myUpate(entity) 这个和 updateById(entity) 是等价的，会提取参数进行乐观锁填充，但更新实现需要自行处理。
         *     在 update(entity, wrapper) 方法中，wrapper 不能复用。
         */
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());


        if (EnvHelper.isDev()){//开发环境
            //interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());//非法SQL拦截插件
            interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());//防全表更新与删除插件
        }


        return interceptor;
    }


}
